package com.mcxx.util;

import lombok.extern.slf4j.Slf4j;
import net.coobird.thumbnailator.Thumbnails;
import net.coobird.thumbnailator.geometry.Positions;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.tomcat.util.buf.HexUtils;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;


@Slf4j
public class ImgUtil {
    public static final int WIDTH_OUT = 1920;
    public static final int HEIGTH_OUT = 1200;
    public static final float QUALITY = 0.5f;
    public static final float OPACITY = 1f;


    public static String getFileName(String filePath){
        String imgName = "";
        if(CommonUtil.invalidArgs(filePath)){
            return imgName;
        }
        String[] ss = filePath.split("/");
        imgName = ss[ss.length-1];
        imgName = imgName.substring(0, imgName.indexOf("."));
        return imgName;
    }


    public static String getFileSuffix(String fileName){
        String suffix = ".png";
        if(CommonUtil.invalidArgs(fileName) || fileName.contains(".") == false){
            return suffix;
        }
        suffix = fileName.substring(fileName.indexOf("."));
        return suffix;
    }


    public static InputStream compress(InputStream inputStream) {
        return ImgUtil.compressWidthSize(inputStream, WIDTH_OUT, HEIGTH_OUT);
    }


    public static InputStream compress4OriginSize(InputStream inputStream) {
        return ImgUtil.compressWidthSize(inputStream, 0, 0);
    }


    public static InputStream compressWidthSize(InputStream inputStream, int outWidth, int outHeight) {
        if(null == inputStream) {
            return null;
        }

        ByteArrayInputStream arrStream = null;
        ByteArrayInputStream imageStream = null;
        ByteArrayInputStream cloneStream = null;
        try {
            byte[] content = ImgUtil.cloneInputStream(inputStream);
            byte[] head = ArrayUtils.subarray(content, 0, 4);
            String type = ImgUtil.getFileType(head);
            if("unknown".equalsIgnoreCase(type)) {
                return new ByteArrayInputStream(content);
            }

            imageStream = new ByteArrayInputStream(content);
            cloneStream = new ByteArrayInputStream(content);

            int[] compressSize = ImgUtil.getCompressImsgSize(imageStream, outWidth, outHeight);
            int width = compressSize[0];
            int height = compressSize[1];

            byte[] compress = ImgUtil.compressWithWaterMark(cloneStream, null, width, height, QUALITY, OPACITY, true);
            if(null != compress && compress.length > 0) {
                arrStream = new ByteArrayInputStream(compress);
            }
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        } finally {
            try {
                inputStream.close();
                if(null != arrStream) {
                    arrStream.close();
                }
                if(null != imageStream) {
                    imageStream.close();
                }
                if(null != cloneStream) {
                    cloneStream.close();
                }
            } catch (IOException e) {
                log.error(e.getMessage(), e);
            }
        }
        return arrStream;
    }


    private static byte[] compressWithWaterMark(InputStream inputStream, File watermarkFile, int width, int height
            , float quality, float opacity, boolean forceSize) {
        if (inputStream == null) {
            return null;
        }

        ByteArrayOutputStream outStream = null;
        try {
            outStream = new ByteArrayOutputStream();
            if (forceSize) {
                if (watermarkFile == null) {
                    Thumbnails.of(inputStream).forceSize(width, height)
                            .outputQuality(quality)
                            .toOutputStream(outStream);
                }
                else {
                    Thumbnails.of(inputStream).forceSize(width, height)
                            .outputQuality(quality)
                            .watermark(Positions.BOTTOM_RIGHT, ImageIO.read(watermarkFile), opacity)
                            .toOutputStream(outStream);
                }
            }
            else {
                if (watermarkFile == null) {
                    Thumbnails.of(inputStream).size(width, height)
                            .outputQuality(quality)
                            .toOutputStream(outStream);
                }
                else {
                    Thumbnails.of(inputStream).size(width, height).outputQuality(quality)
                            .watermark(Positions.BOTTOM_RIGHT, ImageIO.read(watermarkFile), opacity)
                            .toOutputStream(outStream);
                }
            }
            return outStream.toByteArray();
        } catch (IOException e) {
            log.error(e.getMessage(), e);
            return null;
        } finally {
            try {
                inputStream.close();
            } catch (IOException e) {
                log.error(e.getMessage(), e);
            }
            try {
                if (outStream != null) {
                    outStream.close();
                }
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }
        }
    }


    private static int[] getCompressImsgSize(InputStream inputStream, int outWidth, int outHeight) throws IOException {
        int[] scaleSize = new int[2];


        BufferedImage image = ImageIO.read(inputStream);
        int width = image.getWidth();
        int height = image.getHeight();
        if(outWidth != 0 && outHeight != 0) {
            boolean scaled = false;
            if (width > outWidth) {
                float ratio = outWidth / (float) width;
                height = (int) (ratio * (float) height);
                width = outWidth;
                scaled = true;
            }
            if (height > outHeight && !scaled) {
                float ratio = outHeight / (float) width;
                width = (int) (ratio * (float) width);
                height = outHeight;
            }
        }

        scaleSize[0] = width;
        scaleSize[1] = height;
        return scaleSize;
    }


    private static byte[] cloneInputStream(InputStream inputStream) {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        byte[] content = new byte[1024];
        try {
            while (inputStream.read(content) != -1) {
                outputStream.write(content);
            }
        }
        catch (IOException e) {
            log.error(e.getMessage(), e);
        }
        finally {
            try {
                outputStream.close();
            } catch (IOException e) {
                log.error(e.getMessage(), e);
            }
        }

        return outputStream.toByteArray();
    }


    private static String getFileType(byte[] head) {
        String type = HexUtils.toHexString(head).toUpperCase();
        if(type.contains("FFD8FF")) {
            return "jpg";
        }
        if(type.contains("89504E47")) {
            return "png";
        }
        if(type.contains("47494638")) {
            return "gif";
        }
        if(type.contains("424D")) {
            return "bmp";
        }
        return "unknown";
    }

    public static void main(String[] args) throws IOException {
        String path = "D:\\6.jpg";
        File file = new File(path);
        FileInputStream inputStream = new FileInputStream(file);
        InputStream stream = ImgUtil.compress(inputStream);

        String name = file.getName();
        int pos = name.indexOf(".");
        String outName = name;
        if(-1 != pos) {
            outName = name.substring(0, pos) + "_out" + name.substring(pos, name.length());
        }
        else {
            outName = name + "_out";
        }
        FileOutputStream fout = new FileOutputStream(new File(file.getParent() + File.separator + outName));

        byte[] content = new byte[1024];
        int len = 0;
        int off = 0;
        while ((len = stream.read(content)) != -1) {
            fout.write(content, 0, len);
            off += len;
        }

        fout.close();
        stream.close();
        inputStream.close();
    }
}
